use crate::plan_struct::{ID, Plan, PlanSet, Tag};
use crate::utils::{FromRef, RefInto};
use serde::{Deserialize, Deserializer, Serialize};
use std::collections::{BTreeMap, BTreeSet};
use std::mem::ManuallyDrop;
use std::sync::{Arc, RwLock};

#[derive(Deserialize, Serialize, Clone)]
struct SerdePlanSet {
    pub set: BTreeMap<ID, SerdePlan>,
    pub root: ID,
    pub max_id: ID,
}
#[derive(Deserialize, Serialize, Clone)]
struct SerdePlan {
    pub gid: ID,
    pub childs: BTreeSet<ID>,
    pub title: String,
    pub description: String,
    pub tags: Vec<Tag>,
}
impl FromRef<SerdePlanSet> for PlanSet {
    fn from_ref(serde_plan_set: &SerdePlanSet) -> Self {
        let serde_root = serde_plan_set.set.get(&serde_plan_set.root).unwrap();
        let root = Plan {
            gid: serde_root.gid,
            parent: None,
            childs: BTreeMap::new(),
            title: serde_root.title.clone(),
            description: serde_root.description.clone(),
            tags: serde_root.tags.clone(),
        };
        let root = Arc::new(RwLock::new(root));
        let mut plan_set = PlanSet {
            set: ManuallyDrop::new(BTreeMap::new()),
            root: ManuallyDrop::new(root.clone()),
            max_id: serde_plan_set.max_id,
        };
        plan_set.set.insert(serde_root.gid, root.clone());
        let mut dfs_stack_serde_plans: Vec<Arc<RwLock<Plan>>> = vec![root];
        while let Some(parent) = dfs_stack_serde_plans.pop() {
            let parent_gid = parent.write().unwrap().gid;
            for serde_child in serde_plan_set
                .set
                .get(&parent_gid)
                .unwrap()
                .childs
                .iter()
                .map(|id| serde_plan_set.set.get(id).unwrap())
            {
                let child = Plan::create_empty(&parent, serde_child.gid);
                let mut child_writer = child.write().unwrap();
                child_writer.title = serde_child.title.clone();
                child_writer.description = serde_child.description.clone();
                child_writer.tags = serde_child.tags.clone();
                plan_set.set.insert(serde_child.gid, child.clone());
                dfs_stack_serde_plans.push(child.clone());
            }
        }
        plan_set
    }
}
impl From<SerdePlanSet> for PlanSet {
    fn from(serde_plan_set: SerdePlanSet) -> Self {
        serde_plan_set.ref_into()
    }
}
impl FromRef<PlanSet> for SerdePlanSet {
    fn from_ref(plan_set: &PlanSet) -> Self {
        let root_reader = plan_set.root.clone();
        let root_id = root_reader.read().unwrap().gid;
        let mut dfs_stack_plan = vec![root_reader];
        let mut set = BTreeMap::<_, _>::new();
        while let Some(plan) = dfs_stack_plan.pop() {
            let plan_reader = plan.read().unwrap();
            let serde_childs = plan_reader
                .childs
                .iter()
                .map(|(&child_id, child)| {
                    dfs_stack_plan.push(ManuallyDrop::new(child.clone()));
                    child_id
                })
                .collect();
            set.insert(
                plan_reader.gid,
                SerdePlan {
                    gid: plan_reader.gid,
                    childs: serde_childs,
                    title: plan_reader.title.clone(),
                    description: plan_reader.description.clone(),
                    tags: plan_reader.tags.clone(),
                },
            );
        }
        SerdePlanSet {
            root: root_id,
            max_id: plan_set.max_id,
            set,
        }
    }
}
impl From<PlanSet> for SerdePlanSet {
    fn from(plan_set: PlanSet) -> SerdePlanSet {
        plan_set.ref_into()
    }
}
impl<'de> Deserialize<'de> for PlanSet {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        SerdePlanSet::deserialize(deserializer).map(|serde_plan_set| serde_plan_set.into())
    }
}
impl Serialize for PlanSet {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        SerdePlanSet::from_ref(self).serialize(serializer)
    }
}
